﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Json;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using TechTalk.SpecFlow;
using WatiN.Core;
using Table = TechTalk.SpecFlow.Table;
using TableRow = TechTalk.SpecFlow.TableRow;

namespace Sketch.Test.Acceptance
{
    [Binding]
    public class SketchSteps: Steps
    {
        [When(@"I add the element of type ""(.*)""")]
        public void AddElementOfType(string type)
        {
            var list = WebBrowser.Current.List("elements");
            var item = list.OwnListItem(Find.By("data-command", type));
            item.Click();
        }

        [When(@"I select the last added element")]
        public void SelectLastAddedElement()
        {
            WebBrowser.Current.Eval("App.Current.activePage.clickAt(0,0);");
        }

        [Then(@"I should get the following model")]
        public void ValidateModel(Table expected)
        {            
            var model = GetModel();

            var actual = new Table(expected.Header.ToArray());

            AddModelElements(actual, model);

            CompareModels(expected, actual);
        }
        [Then(@"I should get the following view content")]
        public void ValidateContent(Table expected)
        {
            var model = GetModel();
            var actual = new Table(expected.Header.ToArray());
            
            AddModelElements(actual, model);

            var actualContent = new Table(expected.Header.ToArray());
            //skip all containers stuff
            foreach (var row in actual.Rows.Skip(4))
            {
                var rowLocal = row;
                actualContent.AddRow(actualContent.Header.Select(header => rowLocal[header]).ToArray());
            }

            CompareModels(expected, actualContent);
        }

        private static void CompareModels(Table expected, Table actual)
        {
            Assert.AreEqual(expected.RowCount, actual.RowCount, "Number of elements does not match");

            for (var i = 0; i < expected.RowCount; i++)
            {
                var expectedElement = expected.Rows[i]["Element"];
                var actualElement = actual.Rows[i]["Element"];

                Assert.AreEqual(expectedElement, actualElement, ignoreCase: true, message: "Incorrect element");

                var expectedProperties = expected.Rows[i]["Properties"];
                if (!string.IsNullOrEmpty(expectedProperties))
                {
                    var expectedPropertyArray = (JArray)JsonConvert.DeserializeObject(expectedProperties);
                    var actualPropertyArray = (JArray)JsonConvert.DeserializeObject(actual.Rows[i]["Properties"]);

                    foreach (var p in expectedPropertyArray)
                    {
                        var name = p["name"].ToString();
                        var value = p["value"].ToString();

                        var actualProperty = actualPropertyArray.Single(x => x["name"].ToString().Equals(name, StringComparison.OrdinalIgnoreCase));
                        Assert.IsNotNull(actualProperty, "Property {0} not found", name);
                        Assert.AreEqual(value, actualProperty["value"].ToString(), ignoreCase: true, message: "Incorrect value for property {0}", parameters: name);
                    }
                }
            }
        }

        private static JObject GetModel()
        {
            var json = WebBrowser.Current.Eval("App.Current.activePage.generateModel();");
            return (JObject)JsonConvert.DeserializeObject(json);
        }

        private static void WalkSimpleProperties(IEnumerable<JToken> properties, Action<JToken, string> action, string prefix = null)
        {
            foreach (var p in properties)
            {
                var fullName = prefix;
                var complex = p["value"] as JArray;
                var isComplex = complex != null;

                if (string.IsNullOrEmpty(fullName))
                    fullName = p["name"].ToString();
                else
                    fullName += "." + p["name"];                               

                if (isComplex)
                    WalkSimpleProperties(complex, action, fullName);
                else
                    action(p, fullName);
            }
        }

        private static void AddModelElements(Table model, JObject element)
        {
            var type = element["type"].ToString();
            var properties = element["properties"];
            
            model.AddRow(type, properties != null ? properties.ToString() : string.Empty);
            
            var children = (JArray) element["children"];
            if (children != null)
                foreach (JObject child in children)
                {
                    AddModelElements(model, child);
                }
        }

        private static string GetPropertyControlName(string property)
        {
            return property + "_input";
        }

        [When(@"I set property ""(.*)"" using checkbox to ""(.*)""")]
        public void SetPropertyUsingCheckbox(string name, bool value)
        {
            var checkbox = WebBrowser.Current.CheckBox(GetPropertyControlName(name));
            checkbox.Checked = value;
        }
        [When(@"I set property ""(.*)"" using dropdown to ""(.*)""")]
        public void SetPropertyUsingDropDown(string name, string value)
        {
            var id = GetPropertyControlName(name);
            var dropdown = WebBrowser.Current.SelectList(id);
            dropdown.SelectByValue(value);
            WebBrowser.Current.Eval("$('#" + id + "').change();");
        }
        [Then(@"The value of the checkbox editor for property ""(.*)"" should be ""(.*)""")]
        public void ValidateCheckboxPropertyEditor(string property, bool value)
        {
            var checkbox = WebBrowser.Current.CheckBox(GetPropertyControlName(property));
            Assert.AreEqual(value, checkbox.Checked, "The editor for property {0} has incorrect value", property);
        }
        [When(@"I undo the last action")]
        public void Undo()
        {
            WebBrowser.Current.Link(Find.ByClass("undo")).Click();
        }
    }
}